﻿using Cms.Application.Admin.Presenters;
using Cms.Application.Admin.Views;
using Cms.Domain;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace Cms.Tests.Unit.Application.Admin.Presenters
{
    [TestClass]
    public class LoginPresenterTests : BasePresenterTests
    {
        private Administrator _admin;
        private Mock<ILoginView> _viewMock;
        
        private void Prepare()
        {
            InitRepositoryFactoryMock();
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator(1)).Returns(_admin);
            _viewMock = new Mock<ILoginView>();
            _viewMock.Setup(v => v.ContextTranslationFile).Returns("Admin/Translations/Login.xml");
            _viewMock.Setup(v => v.Localization).Returns("en-US");
            _viewMock.SetupAllProperties();
        }
        
        [TestMethod]
        public void SystemInstallationAutoLogin_HardcodedCredentials_RedirectToConfigureSite()
        {
            Prepare();
            // Emulate how things are set up right after the database is created
            _admin = new Administrator() { ID = 1, Username = "admin", Password = "admin", CurrentSite = null };
            AdministratorGroup group = new AdministratorGroup() { ID = 1 };
            _admin.Group = group;
            string encryptedCredentials = Cms.Utilities.Cipher.Encrypt(_admin.Username + "\t" + _admin.Password);
            _viewMock.Setup(v => v.AutoLoginEncryptedCredentials).Returns(encryptedCredentials);
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator("admin", "admin")).Returns(_admin);
            LoginPresenter presenter = new LoginPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            _viewMock.Verify(m => m.ConfigureFirstSite(_admin));
        }
        
        [TestMethod]
        public void Initialize_CookieCredentialsExists_CredentialsAreSet()
        {
            Prepare();
            // Emulate how things are set up right after the database is created
            _admin = new Administrator() { ID = 1, Username = "admin", Password = "admin", CurrentSite = null };
            string savedEncryptedCredentials = Cms.Utilities.Cipher.Encrypt(_admin.Username + "\t" + _admin.Password);
            _viewMock.Setup(v => v.SavedEncryptedCredentials).Returns(savedEncryptedCredentials);
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator("admin", "admin")).Returns(_admin);
            LoginPresenter presenter = new LoginPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Initialize();
            Assert.AreEqual("admin", _viewMock.Object.Username);
            Assert.AreEqual("admin", _viewMock.Object.Password);
            Assert.IsTrue(_viewMock.Object.RememberCredentials);
        }
        
        [TestMethod]
        public void Authenticate_NormalLogin_UserIsLoggedIn()
        {
            Prepare();
            // Emulate how things are set up right after the database is created
            _admin = new Administrator() { ID = 1, Username = "admin", Password = "admin", CurrentSite = null };
            Site site = new Site() { Domain = "www.xxx.com" };
            _admin.AvailableSites.Add(site);
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator("admin", "admin")).Returns(_admin);
            LoginPresenter presenter = new LoginPresenter(_viewMock.Object, FactoryMock.Object);
            _viewMock.Object.Username = _admin.Username;
            _viewMock.Object.Password = _admin.Password;
            _viewMock.SetupGet(p => p.RequestedDomain).Returns("www.xxx.com");
            presenter.Authenticate();
            _viewMock.Verify(m => m.Login(_admin));
        }
        
        [TestMethod]
        public void Authenticate_NoAvailableSiteAndUserCanCreateSites_RedirectToConfigureSite()
        {
            Prepare();
            // Emulate how things are set up right after the database is created
            _admin = new Administrator() { ID = 1, Username = "admin", Password = "admin", CurrentSite = null };
            AdministratorGroup group = new AdministratorGroup() { ID = 1 };
            _admin.Group = group;
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator("admin", "admin")).Returns(_admin);
            LoginPresenter presenter = new LoginPresenter(_viewMock.Object, FactoryMock.Object);
            _viewMock.Object.Username = _admin.Username;
            _viewMock.Object.Password = _admin.Password;
            presenter.Authenticate();
            _viewMock.Verify(m => m.ConfigureFirstSite(_admin));
        }
        
        [TestMethod]
        public void Authenticate_NoAvailableSiteAndUserCanNotCreateSites_NoSiteError()
        {
            Prepare();
            _admin = new Administrator() { ID = 1, Username = "admin", Password = "admin", CurrentSite = null };
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator("admin", "admin")).Returns(_admin);
            AdministratorGroup group = new AdministratorGroup() { ID = 2, Name = "Users" };
            // Group is not Administrators and the permissions CreateFirstSite is not set
            _admin.Group = group;
            _viewMock.Object.Username = _admin.Username;
            _viewMock.Object.Password = _admin.Password;
            LoginPresenter presenter = new LoginPresenter(_viewMock.Object, FactoryMock.Object);
            presenter.Authenticate();
            _viewMock.Verify(m => m.DisplayErrorAndPromptForNewLogin());
            Assert.IsTrue(presenter.ValidationErrorCodes.Contains(2));
        }

        [TestMethod]
        public void Authenticate_UserHasCurrentLocaleFromAnotherSite_CurrentLocaleIsUpdatedToThisSitesDefaultLocale()
        {
            Prepare();
            
            // Scenario: 
            // Upon the last visit, the administrator closed the browser using siteB with localeB2.
            // Now, when he logs in on siteA, the default locale should be changed, otherwise the content will not be displayed correctly.

            Site siteA = new Site() { ID = 1, Domain = "www.aaa.com", Online = true };
            Site siteB = new Site() { ID = 2, Domain = "www.bbb.com", Online = true };
            Locale localeA1 = new Locale() { ID = 1, Site = siteA };
            Locale localeA2 = new Locale() { ID = 2, Site = siteA };
            Locale localeB1 = new Locale() { ID = 3, Site = siteB };
            Locale localeB2 = new Locale() { ID = 4, Site = siteB };
            siteA.Locales.Add(localeA1);
            siteA.Locales.Add(localeA2);
            siteB.Locales.Add(localeB1);
            siteB.Locales.Add(localeB2);
            siteA.DefaultLocale = localeA1;
            siteB.DefaultLocale = localeB1;
            _admin = new Administrator() { ID = 1, Username = "admin", Password = "admin", CurrentSite = siteB, CurrentSiteLocale = localeB2, DefaultSite = siteA };
            _admin.AvailableSites.Add(siteA);
            _admin.AvailableSites.Add(siteB);
            AdministratorRepositoryMock.Setup(a => a.GetAdministrator("admin", "admin")).Returns(_admin);
            LoginPresenter presenter = new LoginPresenter(_viewMock.Object, FactoryMock.Object);
            _viewMock.Object.Username = _admin.Username;
            _viewMock.Object.Password = _admin.Password;
            _viewMock.SetupGet(p => p.RequestedDomain).Returns("www.aaa.com");
            presenter.Authenticate();
            Assert.AreEqual(1, _admin.CurrentSiteLocale.ID);
        }
    }
}
